package org.dreamwork.cai.core;

import org.dreamwork.cai.io.AbstractRouteBroadcaster;
import org.dreamwork.cai.io.AbstractServiceProvider;
import org.dreamwork.cai.io.IRemoteServiceStub;
import org.dreamwork.cai.proxy.IServiceResolver;
import org.dreamwork.compilation.NotSupportedException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

/**
 * CAI 框架获取真正各项服务的工厂类.
 * <p>这个类提供了查找以下三种服务实现的方法：</p>
 * <ul>
 * <li>远程服务的本地存根，系统内置了 {@link org.dreamwork.cai.io.impl.SimpleJsonRemoteServiceStubImpl
 * SimpleJsonRemoteServiceStubImpl} - 基于{@code BIO TCP} 的，通讯协议为 JSON 的简单实现 - 来提供远程调用存根</li>
 * <li>路由广播服务程序，系统内置了 {@link org.dreamwork.cai.io.impl.UdpRouteBroadcaster UdpRouteBroadcaster}
 * - 基于 {@code BIO UDP}的，通讯协议为 JSON 的简单实现 - 来提供路由广播服务</li>
 * <li>本地服务远程提供服务程序，系统内置了 {@link org.dreamwork.cai.io.impl.SimpleProvider SimpleProvider} -
 * 基于 {@code BIO TCP}，协议为 JSON 的简单实现 - 来提供本地服务</li>
 * </ul>
 * 具体实现提供者可以在 classpath:/META-INF/cai.properties 文件中注册相应的实现类来替代框架提供的简单实现。
 * <ul>
 * <li>使用键值 {@code broadcaster} 来指定路由广播提供程序，参见 {@link #IMPL_BROADCASTER}</li>
 * <li>使用键值 {@code remote-stub} 来提供远程服务的本地存根服务程序，参见 {@link #IMPL_REMOTE_STUB}</li>
 * <li>使用键值 {@code service-provider} 来提供本地服务提供程序，参见 {@link #IMPL_SERVICE_PROVIDER}</li>
 * </ul>
 */
public class RemoteServiceFactory {
    private static final Logger logger = LoggerFactory.getLogger (RemoteServiceFactory.class);

    private static final Properties props = new Properties ();
    private static final Map<Class<?>, String> DEFAULT_IMPL = new HashMap<> ();

    public static final String IMPL_BROADCASTER      = "broadcaster",
                               IMPL_REMOTE_STUB      = "remote-stub",
                               IMPL_SERVICE_PROVIDER = "service-provider";

    private static IRemoteServiceStub service;
    private static AbstractRouteBroadcaster broadcaster;
    private static AbstractServiceProvider provider;

    static {
        DEFAULT_IMPL.put (IRemoteServiceStub.class, "org.dreamwork.cai.io.impl.SimpleJsonRemoteServiceStubImpl");
        DEFAULT_IMPL.put (AbstractRouteBroadcaster.class, "org.dreamwork.cai.io.impl.UdpRouteBroadcaster");
        DEFAULT_IMPL.put (AbstractServiceProvider.class, "org.dreamwork.cai.io.impl.SimpleProvider");

        ClassLoader loader = RemoteServiceFactory.class.getClassLoader ();
        try (InputStream in = loader.getResourceAsStream ("META-INF/cai.properties")) {
            if (in != null) {
                props.load (in);
            }
        } catch (IOException ex) {
            logger.warn (ex.getMessage (), ex);
        }
    }

    /**
     * 获取远程服务存根的工厂方法
     * @return 远程服务存根的实现
     */
    public synchronized static IRemoteServiceStub getRemoteService () {
        if (logger.isTraceEnabled ()) {
            logger.trace ("trying to get remote service ...");
        }
        if (service == null) {
            ClassLoader loader = IRemoteServiceStub.class.getClassLoader ();
            Class<?> type;
            String className = props.getProperty (IMPL_REMOTE_STUB);
            if (className == null) {
                className = DEFAULT_IMPL.get (IRemoteServiceStub.class);
            }
            try {
                type = loader.loadClass (className);
                service = (IRemoteServiceStub) type.newInstance ();
                if (logger.isTraceEnabled ()) {
                    logger.trace ("remote service implementation loaded as: {}", service);
                }
            } catch (ClassNotFoundException ex) {
                // ignore
            } catch (InstantiationException | IllegalAccessException ex) {
                throw new NotSupportedException ();
            }
        }

        return service;
    }

    /**
     * 获取路由广播的实现的工厂方法
     * @param resolver 服务解析器
     * @param port     本地广播发送端口
     * @return 路由广播的实现
     */
    @SuppressWarnings ("unchecked")
    public synchronized static AbstractRouteBroadcaster getRouteBroadcaster (IServiceResolver resolver, int port) {
        if (broadcaster == null) {
            ClassLoader loader = IRemoteServiceStub.class.getClassLoader ();
            Class<AbstractRouteBroadcaster> type;
            String className = props.getProperty (IMPL_BROADCASTER);
            if (className == null) {
                className = DEFAULT_IMPL.get (AbstractRouteBroadcaster.class);
            }
            try {
                type = (Class<AbstractRouteBroadcaster>) loader.loadClass (className);
                Constructor<AbstractRouteBroadcaster> c = type.getConstructor (IServiceResolver.class, int.class);
                broadcaster = c.newInstance (resolver, port);
                if (logger.isTraceEnabled ()) {
                    logger.trace ("remote service implementation loaded as: {}", broadcaster);
                }
            } catch (ClassNotFoundException ex) {
                // ignore
            } catch (InstantiationException | IllegalAccessException | NoSuchMethodException | InvocationTargetException ex) {
                throw new NotSupportedException ();
            }
        }

        return broadcaster;
    }

    /**
     * 获取本地服务提供程序的实现的工厂方法
     * @param resolver 服务解析器
     * @return 本地服务提供服务程序的实现
     */
    @SuppressWarnings ("unchecked")
    public static synchronized AbstractServiceProvider getServiceProvider (IServiceResolver resolver) {
        if (provider == null) {
            ClassLoader loader = AbstractServiceProvider.class.getClassLoader ();
            Class<AbstractServiceProvider> type;
            String className = props.getProperty (IMPL_SERVICE_PROVIDER);
            if (className == null) {
                className = DEFAULT_IMPL.get (AbstractServiceProvider.class);
            }
            try {
                type = (Class<AbstractServiceProvider>) loader.loadClass (className);
                provider = type.newInstance ();
                provider.setServiceResolver (resolver);
            } catch (ClassNotFoundException ex) {
                // ignore
            } catch (InstantiationException | IllegalAccessException ex) {
                throw new NotSupportedException ();
            }
        }

        return provider;
    }
}
